package com.newfiber.common.core.utils;

import com.cronutils.model.Cron;
import com.cronutils.model.CronType;
import com.cronutils.model.definition.CronDefinitionBuilder;
import com.cronutils.model.time.ExecutionTime;
import com.cronutils.parser.CronParser;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author : X.K
 * @since : 2023/2/20 下午2:47
 */
public class CronUtils {

    public static List<Date> getExecutionTime(String cronStr, Integer num) {
        CronParser parser = new CronParser(CronDefinitionBuilder.instanceDefinitionFor(CronType.SPRING));
        Cron cron = parser.parse(cronStr);
        ExecutionTime time = ExecutionTime.forCron(cron);
        ZonedDateTime now = ZonedDateTime.now();
        ZonedDateTime next = getNext(time, now);
        List<Date> resultList = new ArrayList<>(num);

        for (int i = 1; i < num; i++) {
            resultList.add(Date.from(next.toInstant()));
            next = getNext(time, next);
        }

        return resultList;
    }

    public static List<Date> getExecutionTime(String cronStr, Date endDatetime) {
        CronParser parser = new CronParser(CronDefinitionBuilder.instanceDefinitionFor(CronType.QUARTZ));
        Cron cron = parser.parse(cronStr);
        ExecutionTime time = ExecutionTime.forCron(cron);
        ZonedDateTime now = ZonedDateTime.now();
        ZonedDateTime next = getNext(time, now);
        List<Date> resultList = new ArrayList<>();

        while (Date.from(next.toInstant()).before(endDatetime) || Date.from(next.toInstant()).equals(endDatetime)) {
            resultList.add(Date.from(next.toInstant()));
            next = getNext(time, next);
        }
        return resultList;
    }

    public static List<Date> getExecutionTime(String cronStr, Date startDatetime, Date endDatetime) {
        CronParser parser = new CronParser(CronDefinitionBuilder.instanceDefinitionFor(CronType.QUARTZ));
        Cron cron = parser.parse(cronStr);
        ExecutionTime time = ExecutionTime.forCron(cron);
        ZonedDateTime now = ZonedDateTime.now();
        ZonedDateTime next = getNext(time, now);
        List<Date> resultList = new ArrayList<>();

        while (Date.from(next.toInstant()).before(startDatetime)) {
            next = getNext(time, next);
        }

        while (Date.from(next.toInstant()).before(endDatetime) || Date.from(next.toInstant()).equals(endDatetime)) {
            resultList.add(Date.from(next.toInstant()));
            next = getNext(time, next);
        }
        return resultList;
    }

    private static ZonedDateTime getNext(ExecutionTime time, ZonedDateTime current) {
        return time.nextExecution(current).get();
    }

}
